# Define here the models for your spider middleware
#
# See documentation in:
# https://docs.scrapy.org/en/latest/topics/spider-middleware.html
import asyncio

import icecream
from pyppeteer import launch
from scrapy import signals

# useful for handling different item types with a single interface
from itemadapter import is_item, ItemAdapter


class XlSpiderSpiderMiddleware:
    # Not all methods need to be defined. If a method is not defined,
    # scrapy acts as if the spider middleware does not modify the
    # passed objects.

    @classmethod
    def from_crawler(cls, crawler):
        # This method is used by Scrapy to create your spiders.
        s = cls()
        crawler.signals.connect(s.spider_opened, signal = signals.spider_opened)
        return s

    def process_spider_input(self, response, spider):
        # Called for each response that goes through the spider
        # middleware and into the spider.

        # Should return None or raise an exception.
        return None

    def process_spider_output(self, response, result, spider):
        # Called with the results returned from the Spider, after
        # it has processed the response.

        # Must return an iterable of Request, or item objects.
        for i in result:
            yield i

    def process_spider_exception(self, response, exception, spider):
        # Called when a spider or process_spider_input() method
        # (from other spider middleware) raises an exception.

        # Should return either None or an iterable of Request or item objects.
        pass

    def process_start_requests(self, start_requests, spider):
        # Called with the start requests of the spider, and works
        # similarly to the process_spider_output() method, except
        # that it doesn’t have a response associated.

        # Must return only requests (not items).
        for r in start_requests:
            yield r

    def spider_opened(self, spider):
        spider.logger.info('Spider opened: %s' % spider.name)


class XlSpiderDownloaderMiddleware:
    # Not all methods need to be defined. If a method is not defined,
    # scrapy acts as if the downloader middleware does not modify the
    # passed objects.

    @classmethod
    def from_crawler(cls, crawler):
        # This method is used by Scrapy to create your spiders.
        s = cls()
        crawler.signals.connect(s.spider_opened, signal = signals.spider_opened)
        return s

    def process_request(self, request, spider):
        # Called for each request that goes through the downloader
        # middleware.

        # Must either:
        # - return None: continue processing this request
        # - or return a Response object
        # - or return a Request object
        # - or raise IgnoreRequest: process_exception() methods of
        #   installed downloader middleware will be called
        return None

    def process_response(self, request, response, spider):
        # Called with the response returned from the downloader.

        # Must either;
        # - return a Response object
        # - return a Request object
        # - or raise IgnoreRequest
        return response

    def process_exception(self, request, exception, spider):
        # Called when a download handler or a process_request()
        # (from other downloader middleware) raises an exception.

        # Must either:
        # - return None: continue processing this exception
        # - return a Response object: stops process_exception() chain
        # - return a Request object: stops process_exception() chain
        pass

    def spider_opened(self, spider):
        spider.logger.info('Spider opened: %s' % spider.name)


import random


class RandomUserAgentMiddleware():
    def __init__(self):
        self.user_agents = [
            'Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US)',
            'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.2 (KHTML, like Gecko)Chrome/22.0.1216.0 Safari/537.2',
            'Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:15.0) Gecko/20100101Firefox/15.0.1'
        ]

    def process_request(self, request, spider):
        request.headers['User-Agent'] = random.choice(self.user_agents)


import logging, time


# 设置随机延时
class RandomDelayMiddleware(object):
    def __init__(self, delay):
        self.delay = delay

    @classmethod
    def from_crawler(cls, crawler):
        delay = crawler.spider.settings.get("RANDOM_DELAY", 10)
        if not isinstance(delay, int):
            raise ValueError("RANDOM_DELAY need a int")
        return cls(delay)

    def process_request(self, request, spider):
        # delay = random.randint(0, self.delay)
        # ununiform函数生成小数点后多位数字的浮点数
        delay = random.uniform(0, self.delay)
        # 浮点数保留小数点后一位数
        delay = float("%.1f" % delay)
        logging.debug("### random delay: %s s ###" % delay)
        time.sleep(delay)


# selenium自动化中间件
from selenium import webdriver
from logging import getLogger
from scrapy.http import HtmlResponse


class SeleniumMiddleware():
    def __init__(self):
        self.logger = getLogger(__name__)
        self.timeout = random.randint(30,50)
        self.option = webdriver.ChromeOptions()
        # 无头模式
        self.option.add_argument('-headless')
        # 隐藏"Chrome正在受到自动软件的控制"
        self.option.add_argument('disable-infobars')
        #绕过检测
        self.option.add_argument('--disable-blink-features=AutomationControlled')

        self.browser = webdriver.Chrome(options = self.option)
        self.browser.set_window_size(1400, 700)
        self.browser.set_page_load_timeout(self.timeout)

    def process_request(self, request, spider):
        # self.logger.debug('PhantomJS is Starting')
        self.browser.get(request.url)
        body = self.browser.page_source
        icecream.ic(type(body),body)
        return HtmlResponse(url = request.url, body = body, request = request, encoding = 'utf-8', status = 200)

    def __del__(self):
        self.browser.close()


class PyppeteerMiddleware():
    def process_request(self, request, spider):
        page_html = asyncio.get_event_loop().run_until_complete(self.main(request.url))
        return HtmlResponse(url = request.url, body = page_html, request = request, encoding = 'utf-8', status = 200)

    async def main(self, url):
        browser = await launch()
        page = await browser.newPage()
        await page.goto(url)
        time.sleep(3)
        text = await page.content()
        return text
